#include "wmi_utils.h"
#include "wmi_connectionmanager.h"


using namespace std;
using namespace log4cxx;
using namespace log4cxx::xml;
using namespace log4cxx::helpers;

WMI_HResult
WMI_ConnectionManager::createConnection(wstring user,  //  Domain\user
								   wstring password, // password for user
								   wstring pc_name, // IP or host name of the machine
								   wstring conNameSpace, // connection name space cimv2 or default
								   WMI_Connection& resp)
{		

	WMI_HResult whRes ;
	// get Domain and user string
	wstring domain;
	wstring teamUser;
		
	wstring::size_type end = user.find(L"\\");

	if (end != wstring::npos)
	{
		domain.append(user.substr(0, int(end)));
		teamUser.append(user.substr(int(end) + 1, user.length() - 1));
	} else 
	{
		teamUser = user;
		domain = L"";
	}

    HRESULT hres;
	DOMConfigurator::configure("LogConfig.xml");
	LoggerPtr loggerWConnMan(Logger::getLogger("loggerWConnMan"));
	stringstream out;

	// Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &_pLocMng);
 
    if (FAILED(hres))
    {
        
		whRes.setHRESULT(hres);
		whRes.setDescription(IWBEMLOCATOR_CREATION);
        LOG4CXX_ERROR(loggerWConnMan, whRes.toString());
		out.str("");
		out << dec << endl;
		LOG4CXX_ERROR(loggerWConnMan,out.str());
        CoUninitialize();
        return whRes;                 // Program has failed.
    }

	// Step 4: -----------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method


	//if the local connection
	bool local = false;

	// Connect to the remote root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    //---------------------------------------------------------		

	const wchar_t* auxPcName = pc_name.c_str();
	const wchar_t* auxConNameSpace = conNameSpace.c_str();
	const wchar_t* auxUser = user.c_str();
	const wchar_t* auxPassword = password.c_str();

	int len = wcslen(auxPcName) + wcslen(auxConNameSpace) + 5;
	wchar_t* pcConfig = new wchar_t[len];
	swprintf_s(pcConfig, len, CONCAT.c_str(), auxPcName, auxConNameSpace);		

	int lenUser;
	wchar_t* pc_user;

	if(wcscmp(auxPcName, AUX_PC_NAME.c_str()) == 0)
	{		
		local = true;
	}
	else
	{
		lenUser = wcslen(auxPcName) + wcslen(auxUser) + 3;
		pc_user = new wchar_t[lenUser];
		swprintf_s(pc_user, lenUser, CONCAT1.c_str(), auxPcName, auxUser);
	}

	if(!local)
	{
		out.str("");
        out << CONNECT_REMOTE.c_str() << endl;
		LOG4CXX_DEBUG(loggerWConnMan, out.str());
	}
		

    hres = _pLocMng->ConnectServer(
        _bstr_t(pcConfig),
		_bstr_t(local ? NULL : user.c_str()),		// User name
        _bstr_t(local ? NULL : auxPassword),    // User password
        NULL,                                   // Locale             
        NULL,                                   // Security flags
        _bstr_t(true ? NULL : auxPassword),		// Authority        
        NULL,                                   // Context object 
        &_pSvcMng                               // IWbemServices proxy
    );
		
	if (FAILED(hres))
	{
		whRes.setHRESULT(hres);
		whRes.setDescription(NOT_CONNECT);
		out.str("");
		LOG4CXX_ERROR(loggerWConnMan, whRes.toString());
		out.str("");
		out << dec << endl;
		LOG4CXX_ERROR(loggerWConnMan,out.str());
		_pLocMng->Release();     
		CoUninitialize();
		return whRes;                // Program has failed.
	}

	if(!local)
	{
		out.str("");
        out << CON_REMOTE.c_str() << endl;
		LOG4CXX_DEBUG(loggerWConnMan,out.str());
	}
     
	// step 5: --------------------------------------------------
    //Create COAUTHIDENTITY that can be used for setting security on proxy

    COAUTHIDENTITY authIdent;

	if(!local)
	{
        memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
        authIdent.PasswordLength = wcslen (auxPassword);
        authIdent.Password = (USHORT*)auxPassword;        
		authIdent.User = (USHORT*)teamUser.c_str();
		authIdent.UserLength = wcslen(teamUser.c_str());

		authIdent.Domain = (USHORT*)domain.c_str();
		authIdent.DomainLength = wcslen(domain.c_str());
        authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

	}

     _userAcctMng = &authIdent;

    // Step 6: --------------------------------------------------
    // Set security levels on a WMI connection ------------------
    hres = CoSetProxyBlanket(
       _pSvcMng,                           // Indicates the proxy to set
       RPC_C_AUTHN_DEFAULT,            // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_DEFAULT,            // RPC_C_AUTHZ_xxx
       COLE_DEFAULT_PRINCIPAL,         // Server principal name 
       RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE,    // RPC_C_IMP_LEVEL_xxx
       _userAcctMng,                       // client identity
       EOAC_NONE                       // proxy capabilities 
    );

    if (FAILED(hres))
    {

		whRes.setHRESULT(hres);
		whRes.setDescription(PROXY_NOT_SET);

        LOG4CXX_ERROR(loggerWConnMan, whRes.toString());
        _pSvcMng->Release();
        _pLocMng->Release();
        CoUninitialize();
        return whRes;               // Program has failed.
    }

	resp.setUser(teamUser);
	resp.setPassword(password);
	resp.setDomain(domain);
	

	resp.setIWbemServices(_pSvcMng);
	resp.setCoAuthidentity(_userAcctMng);
	resp.setIWbemLocator(_pLocMng);	
		
	whRes.setDescription(W_OK);
	whRes.setHRESULT(hres);
	return whRes;	
}

WMI_HResult
WMI_ConnectionManager::initCOMSecurityConfiguration()
{
	WMI_HResult whRes;
	// Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------
	//WMI_Connection resp;
	DOMConfigurator::configure("LogConfig.xml");
	LoggerPtr loggerWConnMan(Logger::getLogger("loggerWConnMan"));
	stringstream out;
	HRESULT hres;
	
    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
		whRes.setDescription(COM_LIBRARY);
	whRes.setHRESULT(hres);
        LOG4CXX_ERROR(loggerWConnMan, whRes.toString());
        return whRes;                  // Program has failed.
    }

	// Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------
    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IDENTIFY,    // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );
             // Reserved
                      
    if (FAILED(hres))
    {
		cout << hex << "Problemas: 0x" << hres << endl;
		whRes.setDescription(INIT_SECURITY);
		whRes.setHRESULT(hres);
		LOG4CXX_ERROR(loggerWConnMan, out.str());
		out.str("");
		cout << dec << endl;
		LOG4CXX_ERROR(loggerWConnMan, whRes.toString());
        CoUninitialize();
        return whRes;                    // Program has failed.
    }

	whRes.setDescription(W_OK);
		whRes.setHRESULT(hres);
	return whRes;
}

WMI_HResult 
WMI_ConnectionManager::createConnection(wstring user, wstring password, wstring conNameSpace, WMI_Connection& conRes)
{	
	return createConnection(user, password, AUX_PC_NAME, conNameSpace, conRes);
}